Skip to content

feat: server-generated PDF export for task and patient tables#232

Merged
felixevers merged 4 commits into
mainfrom
claude/table-print-pdf
Jun 3, 2026
Merged

feat: server-generated PDF export for task and patient tables#232
felixevers merged 4 commits into
mainfrom
claude/table-print-pdf

Conversation

@felixevers
Copy link
Copy Markdown
Member

Summary

Replaces the sketchy CSS-only printing with a real Print button that produces a clean, paginated PDF of the task/patient table via a new backend endpoint (FastAPI + WeasyPrint) — usable as an offline paper copy.

Backend

  • POST /export/table.pdf (backend/routers/export.py): renders an autoescaped Jinja2 HTML table to PDF with WeasyPrint. The template is A4 with a repeating header row, page numbers, a generated timestamp, helpwave branding and zebra striping; orientation defaults to landscape.
  • WeasyPrint is imported lazily and returns 503 if the native libraries are missing, so the app still boots in environments without them. The endpoint requires an authenticated user.
  • Input normalization (pad/truncate rows to the column count, row/column caps) and a safe download filename.
  • requirements.txt: add weasyprint==63.1 and jinja2==3.1.4.
  • Dockerfile: add the Pango/fontconfig/font runtime libraries WeasyPrint needs (unpinned — see caveat).

Frontend

  • TableExportButton reads the currently visible columns and loaded rows directly from the table instance (useTableStateContext) and posts them to the endpoint, then opens the returned PDF (falls back to download if pop-ups are blocked). Because the client sends the rendered cells, the PDF matches exactly what the user sees and reuses the GraphQL query's existing auth/filters.
  • Wired into the TaskList and PatientList toolbars; added a print translation key across all six locales.

Validation

  • Backend: pytest tests/unit/test_export_pdf.py ✅ (7 tests). In this environment WeasyPrint rendered a real PDF end-to-end (8 KB, %PDF- header). The 3 unrelated test_base_resolvers.py failures are pre-existing on a clean tree (environmental).
  • Frontend: npm run lint (tsc + eslint) ✅, npm run build ✅, npm run check-translations ✅.

Caveats / follow-ups

  • Docker libs are unpinned (pango fontconfig ttf-dejavu); the repo pins all apk versions, so these should be pinned to the target Alpine digest's versions. I couldn't build the image here to determine them.
  • Exports currently loaded rows (what the table has fetched). With infinite scroll that may be a subset of the full filtered set; a follow-up could fetch the entire dataset before export or move row collection server-side.
  • PDF body labels (Rows/Generated/Page) use the backend's English defaults; the client can pass localized labels later.

Generated by Claude Code

claude added 4 commits June 3, 2026 16:29
Adds a Print button to the task and patient tables that produces a clean,
paginated PDF of the current view via a new backend endpoint, so a ward list
can be printed or kept as an offline paper copy.

Backend:
- New POST /export/table.pdf endpoint (routers/export.py) that renders an
  autoescaped Jinja2 HTML table (A4, repeating header, page numbers, generated
  timestamp, helpwave branding) to PDF with WeasyPrint. WeasyPrint is imported
  lazily and returns 503 if the native libraries are unavailable, so the app
  still boots without them. The endpoint requires an authenticated user.
- Row/column normalization (pad/truncate to column count, caps) and a safe
  download filename.
- Add weasyprint + jinja2 to requirements.txt and the Pango/fontconfig/font
  runtime libraries to the backend Dockerfile.
- Unit tests for HTML rendering, escaping, normalization and filename safety.

Frontend:
- TableExportButton reads the visible columns and loaded rows straight from the
  table instance and posts them to the endpoint, opening the returned PDF.
- Wire the button into the TaskList and PatientList toolbars; add a `print`
  translation key across all locales.
…3018)

Pin the Pango/fontconfig/font runtime libraries to their Alpine 3.22 versions
(matching the base image) and use the current font-dejavu package name instead
of the renamed ttf-dejavu.
…anch

The earlier pin used Alpine v3.22 versions, but the base image tracks a newer
branch (gcc 15.2.0-r2), so apk could not satisfy the constraints and the image
build failed. Leave the Pango/fontconfig/font packages unpinned with an
explicit hadolint ignore (DL3018) so they resolve against the base image's own
Alpine branch at build time.
# Conflicts:
#	web/components/tables/PatientList.tsx
#	web/components/tables/TaskList.tsx
@felixevers felixevers merged commit 62d10d4 into main Jun 3, 2026
18 checks passed
@felixevers felixevers deleted the claude/table-print-pdf branch June 3, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants